﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace XamlingCore.SL.Controls.Lists.Lazy
{
    public class XLazyListBox : ListBox
    {
        private ScrollViewer scroller;

        public event EventHandler ExtentHeightReached;
        public event EventHandler ExtentWidthReached;

        private double previousExtentHeight = -1;
        private double previousExtentWidth = -1;

        void scroller_LayoutUpdated(object sender, EventArgs e)
        {
            DoRelocation();
        }

        public double VerticalOffset
        {
            get { return (double)GetValue(VerticalOffsetProperty); }
            set { SetValue(VerticalOffsetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for VerticalOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty VerticalOffsetProperty =
            DependencyProperty.Register("VerticalOffset", typeof(double), typeof(XLazyListBox), new PropertyMetadata(0D, updateVerticalOffset));

      


        public double HorizontalOffset
        {
            get { return (double)GetValue(HorizontalOffsetProperty); }
            set { SetValue(HorizontalOffsetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HorizontalOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.Register("HorizontalOffset", typeof(double), typeof(XLazyListBox), new PropertyMetadata(0D, updateHorizonalOffset));

        private static void updateHorizonalOffset(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var list = d as XLazyListBox;
            list.Scroller.ScrollToHorizontalOffset((double)e.NewValue);
        }

        private static void updateVerticalOffset(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var list = d as XLazyListBox;
            list.Scroller.ScrollToVerticalOffset((double)e.NewValue);
        }



        public bool RaiseExtentUpdates
        {
            get { return (bool)GetValue(RaiseExtentUpdatesProperty); }
            set { SetValue(RaiseExtentUpdatesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for RaiseExtentUpdates.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RaiseExtentUpdatesProperty =
            DependencyProperty.Register("RaiseExtentUpdates", typeof(bool), typeof(XLazyListBox), null);

        

        public double ExtentHeight
        {
            get { return (double)GetValue(ExtentHeightProperty); }
            set { SetValue(ExtentHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ExtentHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ExtentHeightProperty =
            DependencyProperty.Register("ExtentHeight", typeof(double), typeof(XLazyListBox), null);

        
        public double ExtentWidth
        {
            get { return (double)GetValue(ExtentWidthProperty); }
            set { SetValue(ExtentWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ExtentWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ExtentWidthProperty =
            DependencyProperty.Register("ExtentWidth", typeof(double), typeof(XLazyListBox), null);



        public double ViewportWidth
        {
            get { return (double)GetValue(ViewportWidthProperty); }
            set { SetValue(ViewportWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ViewportWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ViewportWidthProperty =
            DependencyProperty.Register("ViewportWidth", typeof(double), typeof(XLazyListBox), null);



        public double ViewportHeight
        {
            get { return (double)GetValue(ViewportHeightProperty); }
            set { SetValue(ViewportHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ViewportHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ViewportHeightProperty =
            DependencyProperty.Register("ViewportHeight", typeof(double), typeof(XLazyListBox), null);

        public double ScrollableWidth
        {
            get { return (double)GetValue(ScrollableWidthProperty); }
            set { SetValue(ScrollableWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ScrollableWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScrollableWidthProperty =
            DependencyProperty.Register("ScrollableWidth", typeof(double), typeof(XLazyListBox), null);



        public double ScrollableHeight
        {
            get { return (double)GetValue(ScrollableHeightProperty); }
            set { SetValue(ScrollableHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ScrollableHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScrollableHeightProperty =
            DependencyProperty.Register("ScrollableHeight", typeof(double), typeof(XLazyListBox), null);





        protected virtual void DoRelocation()
        {
            double locationVertical = scroller.VerticalOffset;
            double locationHorizontal = scroller.HorizontalOffset;

            double extentHeight = scroller.ExtentHeight;
            double extentWidth = scroller.ExtentWidth;

            double vpHeight = scroller.ViewportHeight;
            double vpWidth = scroller.ViewportWidth;

            VerticalOffset = scroller.VerticalOffset;
            HorizontalOffset = scroller.HorizontalOffset;

            ExtentWidth = scroller.ExtentWidth;
            ExtentHeight = scroller.ExtentHeight;
            ViewportWidth = scroller.ViewportWidth;
            ViewportHeight = scroller.ViewportHeight;

            ScrollableWidth = scroller.ScrollableWidth;
            ScrollableHeight = scroller.ScrollableHeight;

            if (vpHeight > 0 && extentHeight > 0)
            {
                if (locationVertical + vpHeight >= extentHeight && RaiseExtentUpdates && previousExtentHeight != extentHeight)
                {
                    previousExtentHeight = extentHeight;
                    if (ExtentHeightReached != null)
                    {
                        ExtentHeightReached(this, EventArgs.Empty);
                    }

                    if (ExtentHeightReachedCommand != null)
                    {
                        ExtentHeightReachedCommand.Execute(null);
                    }
                }
            }

            if (vpWidth > 0 && extentWidth > 0)
            {
                if (locationHorizontal + vpWidth >= extentWidth && RaiseExtentUpdates && previousExtentWidth != extentWidth)
                {
                    previousExtentWidth = extentWidth;

                    if (ExtentWidthReached != null)
                    {
                        ExtentWidthReached(this, EventArgs.Empty);
                    }

                    if (ExtentWidthReachedCommand != null)
                    {
                        ExtentWidthReachedCommand.Execute(null);
                    }
                }
            }

        }

        public override void OnApplyTemplate()
        {
            Scroller = GetTemplateChild("ScrollViewer") as ScrollViewer;
            base.OnApplyTemplate();
        }


        public ScrollViewer Scroller
        {
            get { return scroller; }
            set
            {
                if (scroller != null)
                {
                    scroller.LayoutUpdated -= new EventHandler(scroller_LayoutUpdated);
                }

                scroller = value;

                if (scroller != null)
                {
                    scroller.LayoutUpdated += new EventHandler(scroller_LayoutUpdated);
                }
            }
        }

        public ICommand ExtentHeightReachedCommand
        {
            get { return (ICommand)GetValue(ExtentHeightReachedCommandProperty); }
            set { SetValue(ExtentHeightReachedCommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ExtentReachedCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ExtentHeightReachedCommandProperty =
            DependencyProperty.Register("ExtentHeightReachedCommand", typeof(ICommand), typeof(XLazyListBox), null);

        public ICommand ExtentWidthReachedCommand
        {
            get { return (ICommand)GetValue(ExtentWidthReachedCommandProperty); }
            set { SetValue(ExtentWidthReachedCommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ExtentReachedCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ExtentWidthReachedCommandProperty =
            DependencyProperty.Register("ExtentWidthReachedCommand", typeof(ICommand), typeof(XLazyListBox), null);


    }
}
